package com.zkh.myutils.net.mail;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;

import com.zkh.myutils.code.CoderUtils;
import com.zkh.myutils.io.IOUtils;
import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.utils.Regexp;
import com.zkh.myutils.utils.Regexp.ResultSet;
import com.zkh.myutils.utils.Utils;
import com.zkh.myutils.utils.UtilsException;

/**
 * Mime部件对象
 * @author zkh
 */
class MimePart {
	//传输编码类型
	public static final String BASE64 = "base64";
	private static final String QUOTED = "quoted-printale";
	
	//内容类型
	private String contentType;
	//html文本内容
	private String content;
	//附件
	private File file;
	//附件流
	private InputStream inputStream;
	//附件名
	private String filename;
	//纯文本
	private boolean isText;
	//内嵌资源
	private LinkedHashMap<String, String> inlineSourceMap = new LinkedHashMap<>();
	
	/**
	 * 构造方法，设置部件类型为文本
	 * @param content html文本内容
	 */
	public MimePart(StringBuilder content, boolean isText) {
		//为空
		if(content==null) {
			throw new NullPointerException("文本内容不能为null");
		}
		//保存内容
		this.content = content.toString();
		//纯本文标识
		this.isText = isText;
		//保存类型
		this.contentType = "text/" + (isText ? "plain" : "html") + "; charset=utf-8";
	}
	
	/**
	 * 构造方法，设置部件为附件
	 * @param file 附件File对象
	 */
	public MimePart(File file) {
		//为空或不存在
		if(file==null || !file.isFile()) {
			throw new NullPointerException("附件不能为空或不存在");
		}
		//保存
		this.file = file;
		//附件名
		this.filename = formatString(file.getName(), true);
		//保存类型
		this.contentType = getContentType(file.getAbsolutePath());
	}
	
	/**
	 * 构造方法
	 * @param is 附件流
	 * @param filename 附件名称
	 */
	public MimePart(InputStream is, String filename) {
		this.inputStream = is;
		this.filename = filename;
		this.contentType = "application/octet-stream";
	}
	
	/**
	 * 设置文件名，针对附件，默认是从File对象中获取
	 * @param filename
	 */
	public void setFilename(String filename) {
		this.filename = filename;
	}

	/**
	 * 转换为字符串
	 * @param separator 分隔符
	 */
	public String toString(String separator){
		//文件标识
		boolean isFile = file!=null || inputStream!=null;
		//内容
		StringBuilder sb = new StringBuilder();
		//指定类型
		sb.append("Content-Type: ").append(contentType).append("\r\n");
		//指定编码
		sb.append("Content-Transfer-Encoding: ").append(isFile ? BASE64 : QUOTED).append("\r\n");
		//正文
		if(isFile) {
			//附件标识
			sb.append("Content-Disposition: attachment;\r\n\tfilename=\"" + filename + "\"").append("\r\n");
			//附件流
			InputStream fis = inputStream;
			try {
				//如果是文件，则读取流
				if(file!=null) {
					fis = new FileInputStream(file);
				}
				//拼接并文件转Base64
				sb.append("\r\n").append(CoderUtils.encodeBase64(fis)).append("\r\n");
			}catch(IOException e) {
				//打印异常信息
				Logger.error(e);
				//抛出异常
				throw new UtilsException("生成邮件数据时发生异常");
			} finally {
				//关闭流
				IOUtils.closeQuietly(fis);
			}
		}else {
			//解析HTML中的资源
			if(!isText) {
				//匹配资源
				List<ResultSet> resultSet = Regexp.getResultSet("MAILSRC\\(.+?\\)", content, true);
				//处理资源（从后往前处理）
				for(int i=resultSet.size()-1; i>=0; i--) {
					//当前匹配
					ResultSet rs = resultSet.get(i);
					//取资源
					String filePath = rs.getResult().substring(8, rs.getResult().length()-1);
					//获取cid
					String cid = inlineSourceMap.computeIfAbsent(filePath, Utils::md5);
					//替换
					content = content.substring(0, rs.getStart()) + "cid:" + cid + content.substring(rs.getEnd());
				}
			}
			//保存内容
			sb.append("\r\n").append(content).append("\r\n");
			
			/* 处理引用内嵌资源 */
			//遍历资源
			for(Entry<String, String> kv: inlineSourceMap.entrySet()) {
				//文件
				File file = new File(kv.getKey());
				//不存在
				if(!file.isFile()) {
					throw new UtilsException("文件不存在：" + kv.getKey());
				}
				//获取类型
				String contentType = getContentType(file.getAbsolutePath());
				//分隔符
				sb.append("--" + separator).append("\r\n");
				//指定类型
				sb.append("Content-Type: ").append(contentType).append("\r\n");
				//指定编码
				sb.append("Content-Transfer-Encoding: ").append(BASE64).append("\r\n");
				//指定内嵌
				sb.append("Content-Disposition: inline").append("\r\n");
				//内容ID
				sb.append("Content-ID: " + kv.getValue()).append("\r\n");
				try {
					//文件转Base64
					FileInputStream fis = new FileInputStream(file);
					//拼接
					sb.append("\r\n").append(CoderUtils.encodeBase64(fis)).append("\r\n");
					//关闭流
					IOUtils.closeQuietly(fis);
				}catch(IOException e) {
					Logger.error(e);
					throw new UtilsException("生成邮件数据时发生异常");
				}
			}
		}
		//返回字符串
		return sb.toString();
	}
	
	/**
	 * 格式化字符，首先进行base64编码，然后再附加字符集和编码
	 * @param str 待处理的字符串
	 * @param base64 是否base64编码，false为quoted-printale编码
	 * @return
	 */
	public static String formatString(String str, boolean base64) {
		return "=?utf-8?" + (base64 ? "B" : "Q") + "?" + CoderUtils.encodeBase64(str) + "?=";
	}
	
	/**
	 * 获取文件ContentType，如果获取不到，将默认为application/octet-stream
	 * @param absolutePath 绝对路径
	 * @return
	 */
	private String getContentType(String absolutePath) {
		//获取类型
		String type = "application/octet-stream";
		try {
			type = Files.probeContentType(Paths.get(absolutePath));
		} catch (IOException e) {
			Logger.error("文件类型获取失败");
		}
		//返回类型
		return type;
	}
}
